/*
PearReview - The peer review assistant.
Copyright (C) 2009  Dimitrij Pankratz, Anton Musichin
http://www.pearreview.com, info@pearreview.com

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package com.pearreview.ui.accordion;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

import com.pearreview.ui.slidebox.SlideOpenButton;

import java.util.*;

public class Accordion extends JPanel implements ActionListener {

	private static final long serialVersionUID = 8444383590916482981L;

	/**
	 * The top panel: contains the buttons displayed on the top of the
	 * JOutlookBar
	 */
	private JPanel topPanel = new JPanel(new GridLayout(1, 1));

	/**
	 * The bottom panel: contains the buttons displayed on the bottom of the
	 * JOutlookBar
	 */
	private JPanel bottomPanel = new JPanel(new GridLayout(1, 1));

	/**
	 * A LinkedHashMap of bars: we use a linked hash map to preserve the order
	 * of the bars
	 */
	private Map<String, BarInfo> bars = new LinkedHashMap<String, BarInfo>();

	/**
	 * The currently visible bar (zero-based index)
	 */
	private int visibleBar = 0;

	/**
	 * A place-holder for the currently visible component
	 */
	private JComponent visibleComponent = null;

	/**
	 * Creates a new JOutlookBar; after which you should make repeated calls to
	 * addBar() for each bar
	 */
	public Accordion() {
		this.setLayout(new BorderLayout());
		this.add(topPanel, BorderLayout.NORTH);
		this.add(bottomPanel, BorderLayout.SOUTH);
		setOpaque(false);
		setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
	}

	/**
	 * Adds the specified component to the JOutlookBar and sets the bar's name
	 * 
	 * @param name
	 *            The name of the outlook bar
	 * @param componenet
	 *            The component to add to the bar
	 */
	public void addBar(String name, JComponent component) {
		final BarInfo barInfo = new BarInfo(name, component);
		barInfo.getButton().addActionListener(this);
		
		barInfo.getButton().addMouseListener(new MouseListener() {
		  
		 @Override public void mouseClicked(MouseEvent e) { 
		 }
		  
		 @Override public void mouseEntered(MouseEvent e) { 
		 }
		  
		 @Override public void mouseExited(MouseEvent e) { 
		 }
		 
		 @Override public void mousePressed(MouseEvent e) {
			 Accordion.this.actionPerformed(new
					  ActionEvent(barInfo.getButton(), 1, "open"));
		 }
		 
		 @Override public void mouseReleased(MouseEvent e) { 
		 }
		  
		 });
		this.bars.put(name, barInfo);
		render();
	}

	/**
	 * Removes the specified bar from the JOutlookBar
	 * 
	 * @param name
	 *            The name of the bar to remove
	 */
	/*
	public void removeBar(String name) {
		this.bars.remove(name);
		render();
	}
	*/

	/**
	 * Returns the index of the currently visible bar (zero-based)
	 * 
	 * @return The index of the currently visible bar
	 */
	public int getVisibleBar() {
		return this.visibleBar;
	}

	/**
	 * Programmatically sets the currently visible bar; the visible bar index
	 * must be in the range of 0 to size() - 1
	 * 
	 * @param visibleBar
	 *            The zero-based index of the component to make visible
	 */
	public void setVisibleBar(int visibleBar) {
		if (visibleBar >= 0 && visibleBar < this.bars.size()) {
			this.visibleBar = visibleBar;
			render();
		}
	}
	
	public JComponent[] getBarComponents() {
		Collection<? extends BarInfo> c = bars.values();
		JComponent[] components = new JComponent[c.size()];
		
		int i = 0;
		for(BarInfo inf: c) {
			components[i] = inf.component;
			i++;
		}
		
		return components;
	}

	/**
	 * Causes the outlook bar component to rebuild itself; this means that it
	 * rebuilds the top and bottom panels of bars as well as making the
	 * currently selected bar's panel visible
	 */
	public void render() {
		// Compute how many bars we are going to have where
		int totalBars = this.bars.size();
		int topBars = this.visibleBar + 1;
		int bottomBars = totalBars - topBars;

		// Get an iterator to walk through out bars with
		Iterator<String> itr = this.bars.keySet().iterator();

		// Render the top bars: remove all components, reset the GridLayout to
		// hold to correct number of bars, add the bars, and "validate" it to
		// cause it to re-layout its components
		this.topPanel.removeAll();
		GridLayout topLayout = (GridLayout) this.topPanel.getLayout();
		topLayout.setRows(topBars);
		BarInfo barInfo = null;
		for (int i = 0; i < topBars; i++) {
			String barName = (String) itr.next();
			barInfo = (BarInfo) this.bars.get(barName);
			this.topPanel.add(barInfo.getButton());
			if (i == this.visibleBar) {
				barInfo.getButton().setOpened(true);
			}
		}
		this.topPanel.validate();

		// Render the center component: remove the current component (if there
		// is one) and then put the visible component in the center of this
		// panel
		if (this.visibleComponent != null) {
			this.remove(this.visibleComponent);
		}
		this.visibleComponent = barInfo.getComponent();
		this.add(visibleComponent, BorderLayout.CENTER);

		// Render the bottom bars: remove all components, reset the GridLayout
		// to
		// hold to correct number of bars, add the bars, and "validate" it to
		// cause it to re-layout its components
		this.bottomPanel.removeAll();
		GridLayout bottomLayout = (GridLayout) this.bottomPanel.getLayout();
		bottomLayout.setRows(bottomBars);
		for (int i = 0; i < bottomBars; i++) {
			String barName = (String) itr.next();
			barInfo = (BarInfo) this.bars.get(barName);
			this.bottomPanel.add(barInfo.getButton());
		}
		this.bottomPanel.validate();
		
		this.validate();
	}

	/**
	 * Invoked when one of our bars is selected
	 */
	public void actionPerformed(ActionEvent e) {
		int oldBar = this.visibleBar;
		int currentBar = 0;
		
		if (e != null)
			if (e.getSource() instanceof SlideOpenButton) {
				SlideOpenButton btn = (SlideOpenButton)e.getSource();
				e.setSource(btn.getParent());
			}
		
		for (Iterator<String> i = this.bars.keySet().iterator(); i.hasNext();) 
		{
			String barName = (String) i.next();
			BarInfo barInfo = (BarInfo) this.bars.get(barName);
			if (e != null)
				if (barInfo.getButton() == e.getSource()) {
					// Found the selected button
					this.visibleBar = currentBar;
					barInfo.getButton().setOpened(true);
					// render();
					// return;
				}
			if (currentBar == oldBar) {
				// close the old button
				barInfo.getButton().setOpened(false);
				if (!i.hasNext())
					barInfo.getButton().setOpened(false, true);
			}
			currentBar++;
		}
		render();
	}

	/**
	 * Internal class that maintains information about individual Outlook bars;
	 * specifically it maintains the following information:
	 * 
	 * name The name of the bar button The associated JButton for the bar
	 * component The component maintained in the Outlook bar
	 */
	class BarInfo {
		/**
		 * The name of this bar
		 */
		private String name;

		/**
		 * The JButton that implements the Outlook bar itself
		 */
		private AccordionPanel button;

		/**
		 * The component that is the body of the Outlook bar
		 */
		private JComponent component;

		/**
		 * Creates a new BarInfo
		 * 
		 * @param name
		 *            The name of the bar
		 * @param component
		 *            The component that is the body of the Outlook Bar
		 */
		public BarInfo(String name, JComponent component) {
			this.name = name;
			this.component = component;
			this.button = new AccordionPanel(name);
		}

		/**
		 * Returns the name of the bar
		 * 
		 * @return The name of the bar
		 */
		public String getName() {
			return this.name;
		}

		/**
		 * Sets the name of the bar
		 * 
		 * @param The name of the bar
		 */
		public void setName(String name) {
			this.name = name;
		}

		/**
		 * Returns the outlook bar JButton implementation
		 * 
		 * @return The Outlook Bar JButton implementation
		 */
		public AccordionPanel getButton() {
			return this.button;
		}

		/**
		 * Returns the component that implements the body of this Outlook Bar
		 * 
		 * @return The component that implements the body of this Outlook Bar
		 */
		public JComponent getComponent() {
			return this.component;
		}
	}
}